package com.yupi.project.handler;

import com.yupi.project.model.AuthenticatorResult;
import com.yupi.project.model.ProtectedRequest;
import com.yupi.project.model.RequestHolder;
import com.yupi.project.model.RequestInfo;
import com.yupi.project.security.SecurityVerificationHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;

import static com.yupi.project.model.RedisConstants.REQUEST_NONCE_KEY;
import static com.yupi.project.model.RedisConstants.REQUEST_NONCE_TTL;

/**
 * @description: nonce重放标识以及timeStamp时间戳校验
 * @author SummerBird
 * @date 2024/3/8 19:44
 * @version 1.0
 */
@Component
@Slf4j
public class NonceTimeVerificationHandler implements SecurityVerificationHandler {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("nonce.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    @Override
    public AuthenticatorResult auth(ProtectedRequest protectedRequest) {
        if(!checkTime()){
            return AuthenticatorResult.fail("请求时间戳超时");
        }
        if(!checkNonce(protectedRequest)){
            return AuthenticatorResult.fail("nonce无效或请求重复，请稍后尝试");
        }
        return AuthenticatorResult.success();
    }

    private boolean checkNonce(ProtectedRequest protectedRequest) {
        RequestInfo requestInfo = RequestHolder.get();
        String nonce = requestInfo.getNonce();
        String accessKey = requestInfo.getAccessKey();
        String pathValue = protectedRequest.getRequest().getPath().value();
        // 随机数，防止请求重放
        if(nonce == null || Long.parseLong(nonce) > 1000000L){
            return false;
        }else {// 检验是否重放
            String key = REQUEST_NONCE_KEY + "ak_" + accessKey + ":path_" + pathValue;
            Long isAlive = stringRedisTemplate.execute(
                    SECKILL_SCRIPT,
                    Collections.emptyList(),
                    key, REQUEST_NONCE_TTL.toString(), nonce
            );
            if(isAlive == 0){// 0，说明已经存在该值，说明重放了，拒绝请求
                return false;
            }
        }
        return true;
    }

    private boolean checkTime(){
        String timestamp = RequestHolder.get().getTimestamp();
        //时间戳相差不大于5分钟
        long currentTime = System.currentTimeMillis() / 1000;
        final long FIVE_MINUTES = 5 * 6000L;
        if(timestamp == null || currentTime - Long.parseLong(timestamp) > FIVE_MINUTES){
            return false;
        }
        return true;
    }

    @Override
    public int getOrder() {
        return 7;
    }
}
